#include <windows.h>

#include <GL/gl.h>

#ifndef PFD_GENERIC_ACCELERATED
#define PFD_SWAP_LAYER_BUFFERS      0x00000800
#define PFD_GENERIC_ACCELERATED     0x00001000
#define PFD_SUPPORT_DIRECTDRAW      0x00002000
#endif

#include "sqVirtualMachine.h"
#include "sqConfig.h"
#include "sqPlatformSpecific.h"

typedef long long INT64;
typedef long INT32;

#include "OpenVGPlugin.h"


HMODULE vgLib = NULL;
#include "wglext.h"

/* Plugin refs */
extern struct VirtualMachine *interpreterProxy;

int ioLoadVGLibraryLength(char * libName, int libNameLen)
{
	char * name = alloca(libNameLen + 1);
	if (!name) return 0;

	memcpy(name, libName,libNameLen);
	name[libNameLen] = 0;
	vgLib = LoadLibrary(name);

	if (!vgLib) return 0;

	/* check that we at least having vgGetString function */
	if (!ioGetVGSymbolPtr("vgGetString"))
	{
		FreeLibrary(vgLib);
		vgLib = 0;
		return 0;
	}
	return 1;
}

int ioIsVGLibraryLoaded()
{
	return vgLib != NULL;
};

void * ioGetVGSymbolPtr(char * symName)
{
	if (vgLib)
		return (void*)GetProcAddress(vgLib, symName);
	else
		return 0;
};


/* To test if multisampling available, first we creating a fake window, initializing opengl there and testing for it
*/

#include <stdio.h>

#ifndef GL_MULTISAMPLE 
	#define GL_MULTISAMPLE 0x809D
#endif

static PFNWGLGETEXTENSIONSSTRINGARBPROC _wglewGetExtensionsStringARB = NULL;
static PFNWGLGETEXTENSIONSSTRINGEXTPROC _wglewGetExtensionsStringEXT = NULL;

void loadProcs()
{
	_wglewGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
	_wglewGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT");
}


int vgLogp(const char * format, ...) {
	va_list al;
	FILE * f;
	va_start(al, format);

	f = fopen("sqOpenVG.log", "a");
	if (f) {
		vfprintf(f, format, al);
		fclose(f);
	}
	va_end(al);
  return 1;
}

void vgLog(const char * msg)
{
	vgLogp("%s\n", msg);
}

/*
typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC) (HDC hdc);
typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC) (void);
*/


int wglisExtensionSupported(const char *extension, HDC dc)
{
	const size_t extlen = strlen(extension);
	const char *supported = NULL;
	const char* p = NULL;
	// Try To Use wglGetExtensionStringARB On Current DC, If Possible

	loadProcs();

	if (_wglewGetExtensionsStringARB)
		supported = _wglewGetExtensionsStringARB(dc);

	if (supported == NULL && _wglewGetExtensionsStringEXT)
		supported = _wglewGetExtensionsStringEXT();

	// If That Failed, Try Standard Opengl Extensions String
	if (supported == NULL)
		supported = (char*)glGetString(GL_EXTENSIONS);

	// If That Failed Too, Must Be No Extensions Supported
	if (supported == NULL)
		return 0;

	vgLogp("\n\nWGL extensions: \n%s\n\n", supported);
	
	// Begin Examination At Start Of String, Increment By 1 On False Match
	for (p = supported; ; p++)
	{
		// Advance p Up To The Next Possible Match
		p = strstr(p, extension);

		if (p == NULL)
			return 0;						// No Match

		// Make Sure That Match Is At The Start Of The String Or That
		// The Previous Char Is A Space, Or Else We Could Accidentally
		// Match "wglFunkywglExtension" With "wglExtension"

		// Also, Make Sure That The Following Character Is Space Or NULL
		// Or Else "wglExtensionTwo" Might Match "wglExtension"
		if ((p==supported || p[-1]==' ') && (p[extlen]=='\0' || p[extlen]==' '))
			return 1;						// Match
	}
}

int chooseMultisampleFormat(int doubleBuffered, HDC dc, int * formatNum) {  

	int pixelFormat;
	int valid;
	unsigned int numFormats;
	PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB;
	float fAttributes[] = {
		0.0f,
		0.0f
	};
	int iAttributes[] = {
		WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
		WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
		WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
		WGL_COLOR_BITS_ARB, 24,
		WGL_ALPHA_BITS_ARB, 8, 
		WGL_DEPTH_BITS_ARB, 16,
		WGL_STENCIL_BITS_ARB, 8,
		WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
		WGL_SAMPLE_BUFFERS_ARB, GL_TRUE,
		WGL_SAMPLES_ARB, 8,
		0, 0
	};

	if (!doubleBuffered) iAttributes[15] = GL_FALSE;

	// get our pixel format
	wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");	
	if (!wglChoosePixelFormatARB) {
		vgLog("wglChoosePixelFormatARB == 0");
		return 0;
	}

	// first we check to see if we can get a pixel format for 8 samples
	valid = wglChoosePixelFormatARB(dc, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
	if (valid && numFormats >= 1) {
		*formatNum = pixelFormat;	
		return 1;
	}

	// our pixel format with 8 samples failed, test for 6 samples
	iAttributes[19] = 6;
	valid = wglChoosePixelFormatARB(dc, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
	if (valid && numFormats >= 1) {
		*formatNum = pixelFormat;	
		return 1;
	}

	// our pixel format with 6 samples failed, test for 4 samples
	iAttributes[19] = 4;
	valid = wglChoosePixelFormatARB(dc, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
	if (valid && numFormats >= 1) {
		*formatNum = pixelFormat;	
		return 1;
	}

	// our pixel format with 4 samples failed, test for 2 samples
	iAttributes[19] = 2;
	valid = wglChoosePixelFormatARB(dc, iAttributes, fAttributes, 1, &pixelFormat, &numFormats);
	if (valid && numFormats >= 1) {
		*formatNum = pixelFormat;	
		return 1;
	}
	vgLog("No suitable multisample pixel format");
	return 0;
}


int vgsetPixelFormat(HDC dc, PIXELFORMATDESCRIPTOR * desc )
{
	int index = ChoosePixelFormat(dc, desc);
	if (index == 0)
		return 0;

	SetPixelFormat(dc, index, desc);
	return 1;
}

int checkMultisampling(PIXELFORMATDESCRIPTOR * desc, int doubleBuffered, int * formatNum)
{
	WNDCLASS wc;
	HINSTANCE hInstance;
	HWND hWnd = 0;
	HDC dc = 0;
	HGLRC rc = 0;
	int index;
	int result = 0;
	// register window class

  /* register window class */
	memset(&wc, 0, sizeof(WNDCLASS));

	wc.style =/* CS_OWNDC | */CS_VREDRAW | CS_HREDRAW;
	wc.lpfnWndProc = DefWindowProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = GetModuleHandle(NULL);
	wc.hIcon = NULL;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = NULL;
	wc.lpszMenuName = NULL;
	wc.lpszClassName = "GLEW";;
	if (0 == RegisterClass(&wc)) return 0;

	hInstance = GetModuleHandle(NULL);

  /* create window */
	hWnd = CreateWindow("GLEW", "GLEW", 0, CW_USEDEFAULT, CW_USEDEFAULT, 
                          CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, 
                          GetModuleHandle(NULL), NULL);
	if (!hWnd)
		goto FAILED;

	dc = GetDC(hWnd);

	if (!vgsetPixelFormat(dc, desc))
		goto FAILED;

	rc = wglCreateContext(dc);
	if(!rc) 
		goto FAILED;

	/* Make the context current */
	if(!wglMakeCurrent(dc, rc)) goto FAILED;

	// see if the string exists in WGL
	if (!wglisExtensionSupported("WGL_ARB_multisample",dc)) {
		vgLog("WGL_ARB_multisample extension not supported");
		return 0;
	}

	result = chooseMultisampleFormat(doubleBuffered, dc, formatNum);

FAILED:
  /* do necessary cleanup */
	if(rc) wglDeleteContext(rc);
	if (hWnd) {
		if(dc) 
			ReleaseDC(hWnd, dc);
		DestroyWindow(hWnd);
	}

	return result;
}

HGLRC hglrc = NULL; 
HDC mainDC = NULL;
int multisampleFormat = 0;
int multisampleSupported = 0;


void sqMainWindowSwapBuffers()
{
  if (mainDC != 0)
	SwapBuffers(mainDC);
}

int sqDisableMainWindowContext()
{
  HWND * mainWindow;
  mainWindow = (HWND*) interpreterProxy->ioLoadFunctionFrom("stWindow","");
  if (mainWindow == 0)
  {
	  return 0;
  }

  wglMakeCurrent(0,0);
  if (hglrc)
	  wglDeleteContext(hglrc);
  hglrc = 0;
  if (mainDC)
	  ReleaseDC(*mainWindow, mainDC);
  mainDC = 0;
  return 1;
}

usqInt sqGetMainContext()
{
	return (usqInt) hglrc;
}

int sqActivateMainWindowContext()
{
	if (!hglrc)
		return 0;
	wglMakeCurrent(mainDC, hglrc);
	return 1;
}

/* GL_ARB_MULTISAMPLE */

int sqEnableMainWindowContext(int doubleBuffered)
{
  int index;
  char *string;
  HWND * mainWindow;
  int depth;
  PIXELFORMATDESCRIPTOR pfd;

  if (hglrc)  /* already enabled */
	return 1;

  mainWindow = (HWND*) interpreterProxy->ioLoadFunctionFrom("stWindow","");

  if (mainWindow == 0)
	  return 0;

  mainDC = GetDC(*mainWindow);
  if(!mainDC) {
    return 0;
  }

  /* Query the native depth so we can choose something appropriate */
  depth = GetDeviceCaps(mainDC, BITSPIXEL);
  /* DescribePixelFormat(mainDC, 1, sizeof(pfd), &pfd);*/
  memset(&pfd, 0 , sizeof(pfd));
  pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
  pfd.nVersion = 1;
  pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_GENERIC_ACCELERATED ;
  if (doubleBuffered)
	pfd.dwFlags |= PFD_DOUBLEBUFFER;
  /*else
	pfd.dwFlags |= PFD_SUPPORT_GDI;  */
  pfd.iPixelType = PFD_TYPE_RGBA;
  pfd.cColorBits = depth;
  pfd.cAlphaBits = 0;
  pfd.cAccumBits = 0;
  pfd.cDepthBits = 16;
  pfd.cStencilBits = 8;
  pfd.cAuxBuffers = 0;
  pfd.iLayerType = PFD_MAIN_PLANE;

  index = ChoosePixelFormat(mainDC, &pfd);

/*  if(chooseMultisampleFormat(doubleBuffered, mainDC, &index)){
	  vgLog("********** multisample supported ***********");
  }
*/
  if (checkMultisampling(&pfd, doubleBuffered, &index))
  {
    vgLog("** multisample for new window is supported **");
  }


  if (index == 0)
	  goto FAILED;  // failed

  /* Try setting the pixel format for the window */
  if (!SetPixelFormat(mainDC, index, &pfd))
  {
	  goto FAILED;
  };

  /* Note: SetPixelFormat may fail if the pixel format has been set before.
     Rather than failing here we do ignore the result of the above,
     so that an old pixel format will be used in any case. */
  index = GetPixelFormat(mainDC);
  DescribePixelFormat(mainDC, index, sizeof(pfd), &pfd);

  if (doubleBuffered && (pfd.dwFlags & PFD_DOUBLEBUFFER) == 0 )
    goto FAILED;

  hglrc = wglCreateContext(mainDC);
  if(!hglrc) {
    goto FAILED;
  }
  /* Make the context current */
  if(!wglMakeCurrent(mainDC, hglrc)) goto FAILED;

  /* use the nicest polygon smoothing mode, since we are to draw the 2D vector graphics */

  glEnable(GL_POLYGON_SMOOTH);
  glHint(GL_LINE_SMOOTH_HINT , GL_NICEST);
  glHint(GL_POLYGON_SMOOTH_HINT , GL_NICEST);
  glHint(GL_POINT_SMOOTH_HINT , GL_NICEST);
  glShadeModel(GL_SMOOTH);
  glEnable(GL_MULTISAMPLE);

  return 1;
FAILED:
  /* do necessary cleanup */
  if(hglrc) wglDeleteContext(hglrc);
  hglrc = 0;
  if(mainDC) ReleaseDC(*mainWindow, mainDC);
  mainDC = 0;
  return 0;
}

